package com.ruoyi.common.pub.util;

import com.ruoyi.common.pub.anno.ExcelCol;
import com.ruoyi.common.pub.anno.ExcelTab;
import com.ruoyi.common.pub.inter.ExcelTemplate;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class ImportExcelUtil {
    public static <T> void importExcel(InputStream inputStream, T type, Consumer<List<T>> consumer) throws Exception {
        Class templateClazz = type.getClass();
        Workbook workbook = null;
        try {
            checkTemplate(templateClazz);
            ExcelTab excelTab = (ExcelTab) templateClazz.getAnnotation(ExcelTab.class);
            int sheetNum = excelTab.sheetNum();
            int startRow = excelTab.startRow();
            workbook = new XSSFWorkbook(inputStream);
            Sheet sheet = workbook.getSheetAt(sheetNum - 1);
            Iterator<Row> rowIterator = sheet.iterator();
            int capacitySize = 500;
            List<T> capacity = new ArrayList<>(capacitySize);
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                if (row.getRowNum() + 1 < startRow) {
                    if (row.getRowNum() == 0) {
                        checkHead(row, templateClazz);
                    }
                    continue;
                }
                T instance = convert(row, templateClazz, sheet);
                if (instance != null) {
                    capacity.add(instance);
                }
                if (capacity.size() >= capacitySize) {
                    consumer.accept(capacity);
                    capacity.clear();
                }
            }
            if (capacity.size() > 0) {
                consumer.accept(capacity);
                capacity.clear();
            }
        } finally {
            if (workbook != null) workbook.close();
            if (inputStream != null) inputStream.close();
        }
    }

    private static boolean isMergedCell(Sheet sheet, Cell cell) {
        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
            CellRangeAddress mergedRegion = sheet.getMergedRegion(i);
            if (mergedRegion.isInRange(cell)) {
                return true;
            }
        }
        return false;
    }

    private static <T> T convert(Row row, Class templateClazz, Sheet sheet) throws Exception {
        T instance = (T) templateClazz.getConstructor().newInstance();
        for (Field field : templateClazz.getDeclaredFields()) {
            field.setAccessible(true);
            ExcelCol excelCol = field.getAnnotation(ExcelCol.class);
            if (excelCol == null) {
                continue;
            }
            String index = excelCol.index();
            Cell cell = row.getCell(getColumnIndexFromLetter(index));
            if (cell == null) {
                continue;
            }
            if (isMergedCell(sheet, cell)) {
                for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
                    CellRangeAddress mergedRegion = sheet.getMergedRegion(i);
                    if (mergedRegion.isInRange(cell)) {
                        cell = sheet.getRow(mergedRegion.getFirstRow()).getCell(mergedRegion.getFirstColumn());
                    }
                }
            }
            if (cell.getStringCellValue().equals("")) {
                continue;
            }
            switch (excelCol.dateType()) {
                case "String": {
                    String value = cell.getStringCellValue();
                    field.set(instance, value);
                }
                break;
                case "date": {
                    //TODO 读取日期数据待开发

                }
                break;
                default: {
                }
            }
        }
        List<ExcelCol>missing = new ArrayList<>();
        int count = 0;
        for (Field field : instance.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            ExcelCol excelCol = field.getAnnotation(ExcelCol.class);
            if (excelCol==null|| !excelCol.requried()) {
                continue;
            }
            count++;
            Object value = field.get(instance);
            if (value==null||value.equals("")) {
                missing.add(excelCol);
            }
        }
        if (missing.size()!=0) {
            if (missing.size()>=count) {
                return null;
            }
            throw new Exception("第"+(row.getRowNum()+1)+"行"+missing.stream().map(ExcelCol::colName).collect(Collectors.joining(","))+"未填");
        }
        return instance;
    }

    private static int getColumnIndexFromLetter(String columnLetter) {
        int columnIndex = 0;
        int length = columnLetter.length();
        for (int i = 0; i < length; i++) {
            columnIndex = columnIndex * 26 + (columnLetter.charAt(i) - 'A' + 1);
        }
        return columnIndex - 1; // 因为POI的列索引从0开始
    }

    private static void checkTemplate(Class<? extends ExcelTemplate> templateClazz) throws Exception {
        ExcelTab excelTab = templateClazz.getAnnotation(ExcelTab.class);
        if (excelTab == null || excelTab.sheetNum() == 0 || excelTab.startRow() == 0) {
            throw new Exception("模板错误");
        }
        boolean flag = true;
        for (Field declaredField : templateClazz.getDeclaredFields()) {
            declaredField.setAccessible(true);
            ExcelCol excelCol = declaredField.getAnnotation(ExcelCol.class);
            if (excelCol != null) {
                flag = false;
            }
        }
        if (flag) {
            throw new Exception("模板错误");
        }
    }

    private static void checkHead(Row row, Class templateClazz) throws Exception {
        for (Field field : templateClazz.getDeclaredFields()) {
            field.setAccessible(true);
            ExcelCol excelCol = field.getAnnotation(ExcelCol.class);
            if (excelCol == null) {
                continue;
            }
            String index = excelCol.index();
            Cell cell = row.getCell(getColumnIndexFromLetter(index));
            String value = cell.getStringCellValue();
            ExcelCol col = field.getAnnotation(ExcelCol.class);
            if (!col.colName().equals(value)) {
                throw new Exception("表头错误");
            }
        }
    }
}
